package com.p7.architect.store.shoppingcart.infrastructure.repositoryimpl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.p7.architect.store.shoppingcart.domain.model.shoppingcart.ShoppingCart;
import com.p7.architect.store.shoppingcart.domain.model.shoppingcart.ShoppingCartId;
import com.p7.architect.store.shoppingcart.domain.model.shoppingcart.ShoppingCartRepository;
import com.p7.architect.store.shoppingcart.domain.model.shoppingcart.ShoppingItem;
import com.p7.architect.store.shoppingcart.domain.model.value.SkuId;
import com.p7.architect.store.shoppingcart.domain.model.value.StoreId;
import com.p7.architect.store.shoppingcart.domain.model.value.UserId;
import com.p7.architect.store.shoppingcart.infrastructure.converter.ShoppingCartDoConverter;
import com.p7.architect.store.shoppingcart.infrastructure.converter.ShoppingItemDoConverter;
import com.p7.architect.store.shoppingcart.infrastructure.repositoryimpl.database.dao.ShoppingCartDao;
import com.p7.architect.store.shoppingcart.infrastructure.repositoryimpl.database.dao.ShoppingItemDao;
import com.p7.architect.store.shoppingcart.infrastructure.repositoryimpl.database.dataobject.ShoppingCartDo;
import com.p7.architect.store.shoppingcart.infrastructure.repositoryimpl.database.dataobject.ShoppingItemDo;
import com.p7.architect.store.shoppingcart.infrastructure.repositoryimpl.diff.Diff;
import com.p7.architect.store.shoppingcart.infrastructure.repositoryimpl.diff.DiffType;
import com.p7.architect.store.shoppingcart.infrastructure.repositoryimpl.diff.EntityDiff;
import com.p7.architect.store.shoppingcart.infrastructure.repositoryimpl.diff.ListDiff;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class ShoppingCartRepositoryImpl extends RepositorySupport<ShoppingCart, ShoppingCartId>
        implements ShoppingCartRepository {

    private final ShoppingCartDao shoppingCartDao;
    private final ShoppingItemDao shoppingItemDao;
    private final ShoppingCartDoConverter shoppingCartDoConverter;
    private final ShoppingItemDoConverter shoppingItemDoConverter;

    public ShoppingCartRepositoryImpl(ShoppingCartDao shoppingCartDao,
                                      ShoppingItemDao shoppingItemDao,
                                      ShoppingCartDoConverter shoppingCartDoConverter,
                                      ShoppingItemDoConverter shoppingItemDoConverter) {
        super(ShoppingCart.class);
        this.shoppingCartDao = shoppingCartDao;
        this.shoppingItemDao = shoppingItemDao;
        this.shoppingCartDoConverter = shoppingCartDoConverter;
        this.shoppingItemDoConverter = shoppingItemDoConverter;
    }

    @Override
    public ShoppingCart findByUserIdAndStoreId(UserId userId, StoreId storeId) {
        QueryWrapper<ShoppingCartDo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId.getId())
                .eq("store_id", storeId.getId());
        ShoppingCartDo shoppingCartDO = shoppingCartDao.getOne(wrapper);
        if (null == shoppingCartDO) {
            return new ShoppingCart(userId, storeId);
        }
        ShoppingCart shoppingCart = getShoppingCart(shoppingCartDO);
        if (shoppingCart != null) {
            this.attach(shoppingCart);
        }
        return shoppingCart;
    }

    private ShoppingCart getShoppingCart(ShoppingCartDo shoppingCartDO) {
        List<ShoppingItemDo> shoppingItemDOList = getItemDosByCartId(shoppingCartDO.getShoppingCartId());
        return shoppingCartDoConverter.convert(shoppingCartDO, shoppingItemDOList);
    }

    private List<ShoppingItemDo> getItemDosByCartId(Long shoppingCartId) {
        return shoppingItemDao.list(new QueryWrapper<ShoppingItemDo>()
                .eq("shopping_cart_id", shoppingCartId));
    }

    @Override
    public ShoppingCart findById(ShoppingCartId shoppingCartId) {
        ShoppingCartDo shoppingCartDO = shoppingCartDao.getById(shoppingCartId.getId());
        if (null == shoppingCartDO) {
            return null;
        }
        return getShoppingCart(shoppingCartDO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    protected ShoppingCartId onInsert(ShoppingCart aggregate) {
        ShoppingCartDo shoppingCartDo = shoppingCartDoConverter.convert(aggregate);
        shoppingCartDao.save(shoppingCartDo);
        ShoppingItem shoppingItem = aggregate.getItems().get(0);
        ShoppingItemDo shoppingItemDo = shoppingItemDoConverter.convert(shoppingItem, aggregate);
        Long shoppingCartId = shoppingCartDo.getShoppingCartId();
        shoppingItemDo.setShoppingCartId(shoppingCartId);
        shoppingItemDao.save(shoppingItemDo);
        return new ShoppingCartId(shoppingCartId);
    }

    @Override
    protected ShoppingCart onSelect(ShoppingCartId shoppingCartId) {
        ShoppingCartDo shoppingCartDO = shoppingCartDao.getById(shoppingCartId.getId());
        if (null == shoppingCartDO) {
            return null;
        }
        return getShoppingCart(shoppingCartDO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    protected void onUpdate(ShoppingCart aggregate, EntityDiff entityDiff) {
        Diff listDiff = entityDiff.getDiff("items");
        if (!(listDiff instanceof ListDiff)) {
            log.info("ShoppingCartRepositoryDiffImpl#onUpdate instanceof failure.");
            return;
        }

        Long shoppingCartId = aggregate.getShoppingCartId().getId();
        ListDiff diffList = (ListDiff) listDiff;
        for (Diff diff : diffList) {
            int diffType = diff.getDiffType();
            if (diffType == DiffType.ADDED.getValue()) {
                ShoppingItem item = (ShoppingItem) diff.getNewValue();
                ShoppingItemDo itemDo = shoppingItemDoConverter.convert(item, aggregate);
                shoppingItemDao.save(itemDo);
                continue;
            }
            if (diffType == DiffType.MODIFIED.getValue()) {
                ShoppingItem item = (ShoppingItem) diff.getNewValue();
                Integer num = item.getItemNum().getNum();
                Long skuId = item.getSkuId().getId();
                UpdateWrapper<ShoppingItemDo> updateWrapper = new UpdateWrapper<>();
                updateWrapper.set("num", num);
                updateWrapper.set("updated_time", LocalDateTime.now());
                updateWrapper.eq("shopping_cart_id", shoppingCartId);
                updateWrapper.eq("sku_id", skuId);
                shoppingItemDao.update(updateWrapper);
                continue;
            }
            if (diffType == DiffType.DELETED.getValue()) {
                ShoppingItem item = (ShoppingItem) diff.getOldValue();
                SkuId skuId = item.getSkuId();
                Map<String, Object> removeMap = new HashMap<>();
                removeMap.put("shopping_cart_id", shoppingCartId);
                removeMap.put("sku_id", skuId.getId());
                shoppingItemDao.removeByMap(removeMap);
            }
        }

        if (entityDiff.isSelfModified()) {
            ShoppingCartDo shoppingCartDo = shoppingCartDoConverter.convert(aggregate);
            shoppingCartDao.updateById(shoppingCartDo);
        }
    }

    @Override
    protected void onDelete(ShoppingCart aggregate, EntityDiff entityDiff) {

    }
}
